home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / sound / alaw sdec scom / a-law compressor.c next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  11.2 KB  |  281 lines

  1. /*
  2.     File:        A-Law compressor.c
  3.  
  4.     Contains:    Sample sound compression component to convert
  5.                 16-bit linear samples to ALaw-encoded data 
  6.  
  7.     Written by: Mark Cookson    
  8.  
  9.     Copyright:    Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved.
  10.  
  11.                 You may incorporate this Apple sample source code into your program(s) without
  12.                 restriction. This Apple sample source code has been provided "AS IS" and the
  13.                 responsibility for its operation is yours. You are not permitted to redistribute
  14.                 this Apple sample source code as "Apple sample source code" after having made
  15.                 changes. If you're going to re-distribute the source, we require that you make
  16.                 it clear in the source that the code was descended from Apple sample source
  17.                 code, but that you've made changes.
  18.  
  19.     Change History (most recent first):
  20.                 8/13/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  21.                 
  22.  
  23. */
  24. #include "ComponentDispatch.h"
  25. #include <Memory.h>
  26. #include <Errors.h>
  27. #include <SoundInput.h>
  28. #include <Components.h>
  29. #include <Gestalt.h>
  30.  
  31. #include <Sound.h>
  32. #include <SoundComponents.h>
  33.  
  34. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  35. // Standard Compression Component Methods
  36. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  37.  
  38. /*    ==============================================================================
  39.     SetSource
  40.  
  41.     This routine sets the component we should call to get more data. We must remember
  42.     this component and also tell it the data format of our component requires.
  43.     ============================================================================== */
  44.  
  45. pascal ComponentResult __SoundComponentSetSource(SoundComponentGlobalsPtr globals, SoundSource sourceID, ComponentInstance source)
  46. {
  47. #pragma unused (sourceID)
  48.  
  49.     globals->sourceComponent = source;                        // remember our source component
  50.     globals->sourceDataPtr = nil;                            // nothing read from source yet
  51.  
  52.     return (noErr);
  53. }
  54.  
  55. /*    ==============================================================================
  56.     SetOutput
  57.  
  58.     This routine sets the data format our component should output. If we can't output
  59.     the requested format, we should return a pointer to the format we do support,
  60.     and return an error, and the Sound Manager will attempt the conversion for us.
  61.     ============================================================================== */
  62.  
  63. pascal ComponentResult __SoundComponentSetOutput(SoundComponentGlobalsPtr globals, SoundComponentDataPtr requested, SoundComponentDataPtr *actual)
  64. {
  65.     globals->outputSamples = requested->sampleCount;        // no. samples to output
  66.     if (globals->outputSamples > kMaxOutputSamples) {        // too much for our buffer
  67.         globals->outputSamples = kMaxOutputSamples;            // only output what we can
  68.     }
  69.  
  70.     // make sure data format and sample sizes match
  71.     if (requested->format == kOutputSampleFormat) {            // formats match
  72.         globals->thisComponent = *requested;
  73.         return (noErr);                                        // no problem outputting this format
  74.     } else {
  75.         // If we can't output the requested format, the Sound Manager will make an attempt to convert our
  76.         // format into something that can be used. In order for the Sound Manager to do this, we need to
  77.         // tell it here the format we will be outputting, so it can setup the proper conversion. This is really
  78.         // handy if your algorithm only outputs 16-bit data, but the Sound Manager is requesting 8-bit. In this
  79.         // case, the Sound Manager will automatically convert your 16-bit data to 8-bit. 
  80.     
  81.         *actual = &globals->thisComponent;                    // tell the Sound Manager what we will output
  82.         return (paramErr);                                    // force the Sound Manager to convert for us
  83.     }
  84. }
  85.  
  86. /*    ==============================================================================
  87.     GetSourceData
  88.  
  89.     This routine is called when the Sound Manager wants your component to compress
  90.     some more data. It should first make sure it has some source data, then
  91.     compress into an internal buffer and return that buffer to the Sound Manager.
  92.     If the Sound Manager is requesting the data in reverse, you must compress
  93.     the data starting from the end of the source buffer, and the Sound Manager will
  94.     reverse the samples for you.
  95.  
  96.     NOTE: This will most often be called at interrupt time.
  97.     ============================================================================== */
  98.  
  99. pascal ComponentResult __SoundComponentGetSourceData(SoundComponentGlobalsPtr globals, SoundComponentDataPtr *resultDataPtr)
  100. {
  101.     SoundComponentDataPtr    sourceDataPtr;
  102.     unsigned long            samplesConverted, framesToConvert, bytesToSkip;
  103.     ComponentResult            result;
  104.     short                    *inputBuffer;
  105.  
  106.     result = noErr;
  107.     sourceDataPtr = globals->sourceDataPtr;                    // get pointer to source sound component
  108.  
  109.     if (sourceDataPtr == nil)                                // source buffer was flushed
  110.     {
  111.         result = PrimeSource(globals);                        // start with all new source data
  112.     }
  113.     else if (sourceDataPtr->sampleCount == 0)                // source buffer is empty
  114.     {
  115.         result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr); // fill it up                    // continue where we left off
  116.     }
  117.  
  118.     if (result != noErr)                                    // bail if error
  119.         return (result);
  120.  
  121.     sourceDataPtr = globals->sourceDataPtr;                    // get pointer to source sound component
  122.  
  123.     if ((sourceDataPtr->format == globals->thisComponent.format) ||    // input and output are same
  124.         (sourceDataPtr->buffer == nil))                        // or no source buffer
  125.     {
  126.         globals->sourceDataPtr = nil;                        // get new source next time
  127.         *resultDataPtr = sourceDataPtr;                        // pass source on down
  128.     }
  129.     else
  130.     {
  131.         // convert the source samples into frames
  132.         framesToConvert = sourceDataPtr->sampleCount / globals->compInfo.samplesPerPacket;
  133.  
  134.         if (framesToConvert)                                // source has some data for us
  135.         {
  136.             if (framesToConvert > globals->outputFrames)
  137.                 framesToConvert = globals->outputFrames;    // limited to size of output
  138.  
  139.             // convert frames back into samples to quantize the source correctly
  140.             samplesConverted = framesToConvert * globals->compInfo.samplesPerPacket;
  141.  
  142.             inputBuffer = (short *)sourceDataPtr->buffer;            // point at input buffer
  143.  
  144.             bytesToSkip = (samplesConverted / globals->compInfo.samplesPerPacket) * globals->compInfo.bytesPerFrame;
  145.             sourceDataPtr->buffer += bytesToSkip;        // skip over the source consumed
  146.  
  147.             sourceDataPtr->sampleCount -= samplesConverted;    // this many samples will be compressed
  148.  
  149.             // Do the compression
  150.             CompressALaw(inputBuffer, globals->buffer, framesToConvert, sourceDataPtr->numChannels);
  151.         }
  152.         else
  153.             samplesConverted = 0;                            // no samples were converted
  154.  
  155.         globals->thisComponent.buffer = (Byte *) globals->buffer;    // data in this buffer
  156.         globals->thisComponent.sampleCount = samplesConverted;        // return num. samples converted
  157.  
  158.         *resultDataPtr = &globals->thisComponent;            // return description of compressed data
  159.     }
  160.  
  161.     return (result);
  162. }
  163.  
  164. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  165. // This routine is used to prime the source. It gets the first load of data
  166. // from the source and intializes the output format and compression factors.
  167.  
  168. ComponentResult PrimeSource(SoundComponentGlobalsPtr globals)
  169. {
  170.     ComponentResult            result;
  171.     SoundComponentDataPtr    sourceDataPtr;
  172.  
  173.     // get data from source
  174.     result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr);
  175.     if (result != noErr)
  176.         return (result);
  177.     if (globals->sourceDataPtr == nil)
  178.         return (paramErr);
  179.  
  180.     sourceDataPtr = globals->sourceDataPtr;
  181.     globals->thisComponent.flags = sourceDataPtr->flags;                // copy flags unchanged
  182.     globals->thisComponent.sampleRate = sourceDataPtr->sampleRate;        // copy sample rate unchanged
  183.     globals->thisComponent.numChannels = sourceDataPtr->numChannels;    // copy numchannels unchanged
  184.  
  185.     globals->compInfo.recordSize = sizeof(CompressionInfo);
  186.     result = GetCompressionInfo(fixedCompression, sourceDataPtr->format,
  187.                                 sourceDataPtr->numChannels, sourceDataPtr->sampleSize,
  188.                                 &globals->compInfo);
  189.  
  190.     globals->destCompInfo.recordSize = sizeof(CompressionInfo);
  191.     globals->destCompInfo.format = globals->thisComponent.format;
  192.     GetCompressorInfo(&globals->destCompInfo);                    // get compression info
  193.     globals->destCompInfo.bytesPerFrame = globals->destCompInfo.bytesPerPacket * sourceDataPtr->numChannels;
  194.  
  195.     globals->outputFrames = globals->outputSamples / globals->compInfo.samplesPerPacket;
  196.  
  197.     return (result);
  198. }
  199.  
  200. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  201. // Compressor-specific Methods
  202. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  203.  
  204. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  205. // This routine initializes the compressor.
  206.  
  207. void InitializeCompressor(SoundComponentGlobalsPtr globals)                // initialize our compressor state
  208. {
  209. #pragma unused (globals)
  210.  
  211.     // Here you should initialize any state used by your compression alorithm (such as predictors)
  212.     // to default values. This routine will be called whenever a new sound is started so you can set
  213.     // up correctly. In our example, we do not have any state, so we have nothing to set up.
  214. }
  215.  
  216. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  217. // This routine returns information about the compression ratios.
  218.  
  219. void GetCompressorInfo(CompressionInfoPtr cp)
  220. {
  221.     if (cp->recordSize > sizeof(CompressionInfo))            // limit amount we return
  222.         cp->recordSize = sizeof(CompressionInfo);
  223.  
  224.     cp->compressionID = fixedCompression;                    // must set this to fixedCompression
  225.     cp->samplesPerPacket = 1;                                // no. samples in one compressed packet
  226.     cp->bytesPerPacket = 1;                                    // no. bytes in a packet
  227.     cp->bytesPerSample = 2;                                    // no. bytes in a compressed sample
  228. }
  229.  
  230. //Code taken from "IMA Recommended Practices for Enhancing Digital Audio Compatibility in Multimedia Systems"
  231. inline short Normalize (short *value) {
  232.     int numShiftBits, msb, nextmsb;
  233.  
  234.     numShiftBits = 0;
  235.     msb = (*value & 0x8000) >> 15;
  236.     nextmsb = (*value & 0x4000) >> 14;
  237.     while (msb == nextmsb) {
  238.         *value <<= 1;
  239.         numShiftBits++;
  240.         msb = (*value & 0x8000) >> 15;
  241.         nextmsb = (*value & 0x4000) >> 14;
  242.     }
  243.     return (numShiftBits);
  244. }
  245.  
  246. //Code taken from "IMA Recommended Practices for Enhancing Digital Audio Compatibility in Multimedia Systems"
  247. void CompressALaw(short *inbuf, Byte *outbuf, unsigned long framesToConvert,
  248.                     unsigned long numChannels)
  249. {
  250.     int                i;
  251.     short            origSample, X, YYY, ZZZZ, numberOfShiftBits;
  252.     unsigned char    newSample;
  253.  
  254.     for (i = 0; i < framesToConvert*numChannels; i++) {        // loop over all source frames
  255.         origSample = *inbuf;                                // get ALaw sample
  256.         inbuf ++;
  257.  
  258.         if (origSample == -32768) {                            //check boundary condition
  259.             newSample = 0x2A;
  260.         } else {
  261.             X = (origSample & 0x8000) >> 8;                    //create the sign bit
  262.             if (origSample & 0x8000)                        //abs(origSample)
  263.                 origSample = -origSample;
  264.             if (origSample < 0x0100) {                        //check for zero segment
  265.                 ZZZZ = (origSample >> 4) & 0x000F;    //if zero segment, shift down 4 to get position
  266.                 YYY = 0x0000;                                //create segment
  267.             } else {                                        //not zero segment
  268.                 numberOfShiftBits = Normalize (&origSample);
  269.                 ZZZZ = (origSample & 0x3C00) >> 10;            //create position
  270.                 YYY = (7 - numberOfShiftBits) << 4;            //create segment
  271.             }
  272.             newSample = X | YYY | ZZZZ;                    //combine sign bit, segment, and position
  273.             newSample ^= 0xD5;                                //invert necessary bits
  274.         }
  275.  
  276.         *outbuf = newSample;                                // output 16-bit sample
  277.         outbuf ++;
  278.     }
  279. }
  280.  
  281.